//
//  GeometryEffectAnimation.swift
//  animations
//
//  Created by Ekaterina on 12.01.2023.
//

import SwiftUI

enum Position {
    case topLeading, topTrailing, bottomTrailing, bottomLeading
}

struct GeometryEffectView: View {
    @State var position = Position.topLeading
    @State var isMoved = false
    
    var body: some View {
        HStack(spacing: 0) {
            VStack {
                Button("Here") { goTo(position: .topLeading) }.buttonStyle(.borderedProminent).disabled(position == Position.topLeading)
                Spacer()
                Button("Here") { goTo(position: .bottomLeading) }.buttonStyle(.borderedProminent).disabled(position == Position.bottomLeading)
            }
            
            GeometryReader { geo in
                VStack {
                    Image("RedImage")
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: geo.size.width / 4)
                        .modifier(SampleEffect(maxHeight: geo.size.height, maxWidth: geo.size.width, position: position, isMoved: isMoved))
                }
                .frame(
                    minWidth: 0,
                    maxWidth: .infinity,
                    minHeight: 0,
                    maxHeight: .infinity,
                    alignment: .topLeading
                )
            }
            .padding(.vertical, 35)
            
            VStack {
                Button("Here") { goTo(position: .topTrailing) }.buttonStyle(.borderedProminent).disabled(position == Position.topTrailing)
                Spacer()
                Button("Here") { goTo(position: .bottomTrailing) }.buttonStyle(.borderedProminent).disabled(position == Position.bottomTrailing)
            }
        }
        .navigationTitle("Geometry Effect")
        .navigationBarTitleDisplayMode(.inline)
        .padding(24)
    }
    
    func goTo(position: Position) {
        withAnimation(.easeInOut(duration: 0.8)) {
            isMoved.toggle()
            self.position = position
        }
    }
}

struct SampleEffect: GeometryEffect {
    var offsetX = 0.0
    var offsetY = 0.0
    
    var maxHeight = 0.0
    var maxWidth = 0.0
    
    var progress = 0.0
    
    let maxStretching = 0.3
    
    var animatableData: AnimatablePair<AnimatablePair<Double, Double>, Double> {
        get { return AnimatablePair(AnimatablePair(offsetX, offsetY), progress) }
        set {
            offsetX = newValue.first.first
            offsetY = newValue.first.second
            progress = newValue.second
        }
    }
    
    init(offsetX: Double = 0.0, offsetY: Double = 0.0, maxHeight: Double = 0.0, maxWidth: Double = 0.0, progress: Double = 0.0, position: Position = Position.topLeading, isMoved: Bool = false) {
        self.offsetX = position == .topLeading || position == .bottomLeading ? 0 : maxWidth - maxWidth / 4
        self.offsetY = position == .topLeading || position == .topTrailing ? 0 : maxHeight - maxWidth / 4
        self.maxHeight = maxHeight
        self.maxWidth = maxWidth
        self.progress = isMoved ? 1 : 0
    }
    
    func effectValue(size: CGSize) -> ProjectionTransform {
        let scaleCoef = 1 + (0.5 - fabs(0.5 - progress))
                
        let transform = CGAffineTransform(1, 0, 0, 1, offsetX, offsetY).scaledBy(x: scaleCoef, y: scaleCoef)
        
        return ProjectionTransform(transform)
    }
}

struct GeometryEffectAnimation_Previews: PreviewProvider {
    static var previews: some View {
        GeometryEffectView()
    }
}
